#ifndef CBLOCK_VIEW_H
#define CBLOCK_VIEW_H

#include "myexception.h"

enum {
	B_BROWN_BLOCK,		// 0
	B_DIRT_BLOCK,		// 1
	B_GRASS_BLOCK,		// 2
	B_PLAIN_BLOCK,		// 3
	B_STONE_BLOCK,		// 4
	B_WALL_BLOCK,		// 5
	B_WOOD_BLOCK,		// 6
	B_WATER_BLOCK,		// 7
	B_SIZE_BLOCK,		// 8		
};

#ifdef __WINDOWS__
typedef unsigned __int16	uint16_t;
#endif

const uint16_t S_SHADOW_WEST		= 1;
const uint16_t S_SHADOW_SOUTH_WEST	= 1 << 1;
const uint16_t S_SHADOW_SOUTH_EAST	= 1 << 2;
const uint16_t S_SHADOW_SOUTH		= 1 << 3;
const uint16_t S_SHADOW_NORTH_WEST	= 1 << 4;
const uint16_t S_SHADOW_NORTH_EAST	= 1 << 5;
const uint16_t S_SHADOW_NORTH		= 1 << 6;
const uint16_t S_SHADOW_EAST		= 1 << 7;
const uint16_t S_SHADOW_SIDE_NORTH	= 1 << 8;
const uint16_t S_SHADOW_SIDE_WEST	= 1 << 9;

const size_t BLOCK_TYPE_SHIFT = 13;

//
// block type is uint16_t:
//	 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
//	+-------------------------+-----+-----+
//	|                         |     |     |
//  +-------------------------+-----+-----+
//                 ^             ^     ^
//	               |             |     |
//            shadows bits      block type
//                               |
//                             reserved for future
//


// shadows sprites: 
const uint16_t shadows_left[] = 
{
	0,
	S_SHADOW_WEST,
	S_SHADOW_SOUTH_WEST,
	S_SHADOW_SOUTH_WEST | S_SHADOW_NORTH,
	S_SHADOW_SOUTH,
	S_SHADOW_NORTH_WEST,
	S_SHADOW_NORTH_WEST | S_SHADOW_SOUTH,
	S_SHADOW_NORTH,
	S_SHADOW_NORTH | S_SHADOW_SOUTH,
	S_SHADOW_WEST | S_SHADOW_NORTH,
	S_SHADOW_WEST | S_SHADOW_SOUTH,
	S_SHADOW_SOUTH_WEST | S_SHADOW_NORTH_WEST,
	S_SHADOW_NORTH | S_SHADOW_SOUTH | S_SHADOW_WEST,
};
const uint16_t shadows_right[] = 
{
	0,
	S_SHADOW_EAST,
	S_SHADOW_SOUTH_EAST,
	S_SHADOW_SOUTH_EAST | S_SHADOW_NORTH,
	S_SHADOW_SOUTH,
	S_SHADOW_NORTH_EAST,
	S_SHADOW_NORTH_EAST | S_SHADOW_SOUTH,
	S_SHADOW_NORTH,
	S_SHADOW_NORTH | S_SHADOW_SOUTH,
	S_SHADOW_EAST | S_SHADOW_NORTH,
	S_SHADOW_EAST | S_SHADOW_SOUTH,
	S_SHADOW_SOUTH_EAST | S_SHADOW_NORTH_EAST,
	S_SHADOW_NORTH | S_SHADOW_SOUTH | S_SHADOW_EAST,
};

const uint16_t shadows_side[] =
{
	0,
	S_SHADOW_SIDE_NORTH,
	S_SHADOW_SIDE_WEST,
	S_SHADOW_SIDE_NORTH | S_SHADOW_SIDE_WEST,
};

const int shadows_left_size = 13;
const int shadows_side_size = 4;


class CBlockView
{
public:
	CBlockView(unsigned char block_type) 
		: BlockType(0)
	{
		BlockType = block_type << BLOCK_TYPE_SHIFT;		
	}

	// Shadows:
	inline void AddShadowWest() {		BlockType |= S_SHADOW_WEST; }
	inline void AddShadowSouthWest() { BlockType |= S_SHADOW_SOUTH_WEST; }
	inline void AddShadowSouthEast() { BlockType |= S_SHADOW_SOUTH_EAST; }
	inline void AddShadowSouth() {		BlockType |= S_SHADOW_SOUTH; }
	inline void AddShadowNorthWest() {	BlockType |= S_SHADOW_NORTH_WEST; }
	inline void AddShadowNorthEast() { BlockType |= S_SHADOW_NORTH_EAST; }
	inline void AddShadowNorth() {		BlockType |= S_SHADOW_NORTH; }
	inline void AddShadowEast() {		BlockType |= S_SHADOW_EAST; }
	inline void AddShadowSideNorth() { BlockType |= S_SHADOW_SIDE_NORTH; }
	inline void AddShadowSideWest() {	BlockType |= S_SHADOW_SIDE_WEST; }


	inline int RightPart() const		// east
	{
		int base = GetBlockType()*shadows_left_size*2;
		if (!IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && !IsShadowSouth() && !IsShadowNorth()) return base;
		if (IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && !IsShadowSouth() && !IsShadowNorth())	return base + 1;
		if (!IsShadowEast()	&& IsShadowSouthEast() && !IsShadowNorthEast() && !IsShadowSouth() && !IsShadowNorth()) return base + 2;
		if (!IsShadowEast() && IsShadowSouthEast() && !IsShadowNorthEast() && !IsShadowSouth() && IsShadowNorth()) return base + 3;
		if (!IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && IsShadowSouth() && !IsShadowNorth()) return base + 4;
		if (!IsShadowEast() && !IsShadowSouthEast() && IsShadowNorthEast() && !IsShadowSouth() && !IsShadowNorth()) return base + 5;
		if (!IsShadowEast() && !IsShadowSouthEast() && IsShadowNorthEast() && IsShadowSouth() && !IsShadowNorth()) return base + 6;
		if (!IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && !IsShadowSouth() && IsShadowNorth()) return base + 7;
		if (!IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && IsShadowSouth() && IsShadowNorth()) return base + 8;
		if (IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && !IsShadowSouth() && IsShadowNorth()) return base + 9;
		if (IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && IsShadowSouth() && !IsShadowNorth()) return base + 10;
		if (!IsShadowEast() && IsShadowSouthEast() && IsShadowNorthEast() && !IsShadowSouth() && !IsShadowNorth()) return base + 11;
		if (IsShadowEast() && !IsShadowSouthEast() && !IsShadowNorthEast() && IsShadowSouth() && IsShadowNorth()) return base + 12;

		throw MyException("unknow shadow bitson right part\n");		
	}

	inline int LeftPart() const		// west
	{
		int base = GetBlockType()*shadows_left_size*2 + shadows_left_size;
		if (!IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && !IsShadowSouth() && !IsShadowNorth()) return base;
		if (IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && !IsShadowSouth() && !IsShadowNorth())	return base + 1;
		if (!IsShadowWest()	&& IsShadowSouthWest() && !IsShadowNorthWest() && !IsShadowSouth() && !IsShadowNorth()) return base + 2;
		if (!IsShadowWest() && IsShadowSouthWest() && !IsShadowNorthWest() && !IsShadowSouth() && IsShadowNorth()) return base + 3;
		if (!IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && IsShadowSouth() && !IsShadowNorth()) return base + 4;
		if (!IsShadowWest() && !IsShadowSouthWest() && IsShadowNorthWest() && !IsShadowSouth() && !IsShadowNorth()) return base + 5;
		if (!IsShadowWest() && !IsShadowSouthWest() && IsShadowNorthWest() && IsShadowSouth() && !IsShadowNorth()) return base + 6;
		if (!IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && !IsShadowSouth() && IsShadowNorth()) return base + 7;
		if (!IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && IsShadowSouth() && IsShadowNorth()) return base + 8;
		if (IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && !IsShadowSouth() && IsShadowNorth()) return base + 9;
		if (IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && IsShadowSouth() && !IsShadowNorth()) return base + 10;
		if (!IsShadowWest() && IsShadowSouthWest() && IsShadowNorthWest() && !IsShadowSouth() && !IsShadowNorth()) return base + 11;
		if (IsShadowWest() && !IsShadowSouthWest() && !IsShadowNorthWest() && IsShadowSouth() && IsShadowNorth()) return base + 12;

		throw MyException("unknow shadow bits on left part\n");
	}

	inline int SidePart() const		// side
	{
		int base = B_SIZE_BLOCK*shadows_left_size*2 + GetBlockType()*shadows_side_size;
		if (!IsShadowSideNorth() && !IsShadowSideWest())  return base;
		if (IsShadowSideNorth() && !IsShadowSideWest()) return base + 1;
		if (!IsShadowSideNorth() && IsShadowSideWest()) return base + 2;

		throw MyException("unknow shadow bits on side part\n");
	}


	inline int GetBlockType() const
	{
		return BlockType >> BLOCK_TYPE_SHIFT;
	}


	void PrintBytes() const
	{
		for (int i = 0; i < 16; ++i)
			printf("%d ", (BlockType >> i) & 1);
		printf("\n");
	}

private:
	uint16_t	BlockType;

	inline uint16_t IsShadowWest() const {		return (BlockType & S_SHADOW_WEST); }
	inline uint16_t IsShadowSouthWest() const {	return (BlockType & S_SHADOW_SOUTH_WEST); }
	inline uint16_t IsShadowSouthEast() const {	return (BlockType & S_SHADOW_SOUTH_EAST); }
	inline uint16_t IsShadowSouth() const {		return (BlockType & S_SHADOW_SOUTH); }
	inline uint16_t IsShadowNorthWest() const {	return (BlockType & S_SHADOW_NORTH_WEST); }
	inline uint16_t IsShadowNorthEast() const {	return (BlockType & S_SHADOW_NORTH_EAST); }
	inline uint16_t IsShadowNorth() const {		return (BlockType & S_SHADOW_NORTH); }
	inline uint16_t IsShadowEast() const {		return (BlockType & S_SHADOW_EAST); }
	inline uint16_t IsShadowSideNorth() const {	return (BlockType & S_SHADOW_SIDE_NORTH); }
	inline uint16_t IsShadowSideWest() const {	return (BlockType & S_SHADOW_SIDE_WEST); }

	
};

#endif // CBLOCK_VIEW_H
